/*
 ------------------------------------------------------------------------------
 Copyright (C) 2013 Eternal Games.

 This file is part of the GLQuake source code.

 The GLQuake source code is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or (at your
 option) any later version.

 The GLQuake source code is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 more details.

 You should have received a copy of the GNU General Public License along with
 the GLQuake source code; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 ------------------------------------------------------------------------------
*/


//
// sg_deltaState.cpp - writes a delta state to the client-game
//


#include "sg_local.h"


/*
 ==================
 SG_WriteDeltaState_General
 ==================
*/
static void SG_WriteDeltaState_General (msg_t *msg, entityState_t *oldState, entityState_t *newState){

	general_t	*oldGeneral = (general_t *)oldState->buffer;
	general_t	*newGeneral = (general_t *)newState->buffer;

	glqMsgSystem->WriteDeltaLong(msg, oldGeneral->contents, newGeneral->contents);
	glqMsgSystem->WriteDeltaLong(msg, oldGeneral->bounds, newGeneral->bounds);
	glqMsgSystem->WriteDeltaBits(msg, oldGeneral->model, newGeneral->model, ES_MODEL_BITS);
	glqMsgSystem->WriteDeltaBits(msg, oldGeneral->clipModel, newGeneral->clipModel, ES_CLIP_MODEL_BITS);
	glqMsgSystem->WriteDeltaBits(msg, oldGeneral->material, newGeneral->material, ES_MATERIAL_BITS);
	glqMsgSystem->WriteDeltaFloat(msg, oldGeneral->materialParms[0], newGeneral->materialParms[0]);
	glqMsgSystem->WriteDeltaFloat(msg, oldGeneral->materialParms[1], newGeneral->materialParms[1]);
	glqMsgSystem->WriteDeltaFloat(msg, oldGeneral->materialParms[2], newGeneral->materialParms[2]);
	glqMsgSystem->WriteDeltaFloat(msg, oldGeneral->materialParms[3], newGeneral->materialParms[3]);
	glqMsgSystem->WriteDeltaFloat(msg, oldGeneral->materialParms[4], newGeneral->materialParms[4]);
	glqMsgSystem->WriteDeltaFloat(msg, oldGeneral->materialParms[5], newGeneral->materialParms[5]);
	glqMsgSystem->WriteDeltaFloat(msg, oldGeneral->materialParms[6], newGeneral->materialParms[6]);
	glqMsgSystem->WriteDeltaFloat(msg, oldGeneral->materialParms[7], newGeneral->materialParms[7]);
	glqMsgSystem->WriteDeltaBits(msg, oldGeneral->sound, newGeneral->sound, ES_SOUND_SHADER_BITS);
	glqMsgSystem->WriteDeltaLong(msg, oldGeneral->soundTime, newGeneral->soundTime);
	glqMsgSystem->WriteDeltaFloat(msg, oldGeneral->soundParms[0], newGeneral->soundParms[0]);
	glqMsgSystem->WriteDeltaFloat(msg, oldGeneral->soundParms[1], newGeneral->soundParms[1]);
	glqMsgSystem->WriteDeltaFloat(msg, oldGeneral->soundParms[2], newGeneral->soundParms[2]);
	glqMsgSystem->WriteDeltaFloat(msg, oldGeneral->soundParms[3], newGeneral->soundParms[3]);
	glqMsgSystem->WriteDeltaFloat(msg, oldGeneral->soundParms[4], newGeneral->soundParms[4]);
	glqMsgSystem->WriteDeltaFloat(msg, oldGeneral->soundParms[5], newGeneral->soundParms[5]);
	glqMsgSystem->WriteDeltaFloat(msg, oldGeneral->soundParms[6], newGeneral->soundParms[6]);
	glqMsgSystem->WriteDeltaFloat(msg, oldGeneral->soundParms[7], newGeneral->soundParms[7]);
}

/*
 ==================

 ==================
*/
static void SG_WriteDeltaState_Mover (msg_t *msg, entityState_t *oldState, entityState_t *newState){

	mover_t	*oldMover = (mover_t *)oldState->buffer;
	mover_t	*newMover = (mover_t *)newState->buffer;

	glqMsgSystem->WriteDeltaBits(msg, oldMover->model, newMover->model, ES_MODEL_BITS);
}

/*
 ==================

 ==================
*/
static void SG_WriteDeltaState_Beam (msg_t *msg, entityState_t *oldState, entityState_t *newState){

}

/*
 ==================
 SG_WriteDeltaState_Light
 ==================
*/
static void SG_WriteDeltaState_Light (msg_t *msg, entityState_t *oldState, entityState_t *newState){

	light_t	*oldLight = (light_t *)oldState->buffer;
	light_t	*newLight = (light_t *)newState->buffer;

	glqMsgSystem->WriteDeltaBits(msg, oldLight->type, newLight->type, 2);
	glqMsgSystem->WriteDeltaVec3(msg, oldLight->center, newLight->center);
	glqMsgSystem->WriteDeltaVec3(msg, oldLight->radius, newLight->radius);
	glqMsgSystem->WriteDeltaFloat(msg, oldLight->xMin, newLight->xMin);
	glqMsgSystem->WriteDeltaFloat(msg, oldLight->xMax, newLight->xMax);
	glqMsgSystem->WriteDeltaFloat(msg, oldLight->yMin, newLight->yMin);
	glqMsgSystem->WriteDeltaFloat(msg, oldLight->yMax, newLight->yMax);
	glqMsgSystem->WriteDeltaFloat(msg, oldLight->zNear, newLight->zNear);
	glqMsgSystem->WriteDeltaFloat(msg, oldLight->zFar, newLight->zFar);
	glqMsgSystem->WriteBool(msg, newLight->noShadows);
	glqMsgSystem->WriteDeltaFloat(msg, oldLight->shadowBias, newLight->shadowBias);
	glqMsgSystem->WriteDeltaFloat(msg, oldLight->shadowSlopeScaleBias, newLight->shadowSlopeScaleBias);
	glqMsgSystem->WriteDeltaFloat(msg, oldLight->shadowSoftness, newLight->shadowSoftness);
	glqMsgSystem->WriteDeltaFloat(msg, oldLight->fogDistance, newLight->fogDistance);
	glqMsgSystem->WriteDeltaFloat(msg, oldLight->fogHeight, newLight->fogHeight);
	glqMsgSystem->WriteDeltaFloat(msg, oldLight->volumeIntensity, newLight->volumeIntensity);
	glqMsgSystem->WriteBool(msg, newLight->volumeShadows);
	glqMsgSystem->WriteBool(msg, newLight->volumeInteractions);
	glqMsgSystem->WriteDeltaBits(msg, oldLight->detailLevel, newLight->detailLevel, 2);
	glqMsgSystem->WriteDeltaBits(msg, oldLight->material, newLight->material, ES_MATERIAL_BITS);
	glqMsgSystem->WriteDeltaFloat(msg, oldLight->materialParms[0], newLight->materialParms[0]);
	glqMsgSystem->WriteDeltaFloat(msg, oldLight->materialParms[1], newLight->materialParms[1]);
	glqMsgSystem->WriteDeltaFloat(msg, oldLight->materialParms[2], newLight->materialParms[2]);
	glqMsgSystem->WriteDeltaFloat(msg, oldLight->materialParms[3], newLight->materialParms[3]);
	glqMsgSystem->WriteDeltaFloat(msg, oldLight->materialParms[4], newLight->materialParms[4]);
	glqMsgSystem->WriteDeltaFloat(msg, oldLight->materialParms[5], newLight->materialParms[5]);
	glqMsgSystem->WriteDeltaFloat(msg, oldLight->materialParms[6], newLight->materialParms[6]);
	glqMsgSystem->WriteDeltaFloat(msg, oldLight->materialParms[7], newLight->materialParms[7]);
}

/*
 ==================
 SG_WriteDeltaState_Speaker
 ==================
*/
static void SG_WriteDeltaState_Speaker (msg_t *msg, entityState_t *oldState, entityState_t *newState){

	speaker_t	*oldSpeaker = (speaker_t *)oldState->buffer;
	speaker_t	*newSpeaker = (speaker_t *)newState->buffer;

	glqMsgSystem->WriteDeltaNormal(msg, oldSpeaker->direction, newSpeaker->direction);
	glqMsgSystem->WriteDeltaBits(msg, oldSpeaker->sound, newSpeaker->sound, ES_SOUND_SHADER_BITS);
	glqMsgSystem->WriteDeltaLong(msg, oldSpeaker->soundTime, newSpeaker->soundTime);
	glqMsgSystem->WriteDeltaFloat(msg, oldSpeaker->soundParms[0], newSpeaker->soundParms[0]);
	glqMsgSystem->WriteDeltaFloat(msg, oldSpeaker->soundParms[1], newSpeaker->soundParms[1]);
	glqMsgSystem->WriteDeltaFloat(msg, oldSpeaker->soundParms[2], newSpeaker->soundParms[2]);
	glqMsgSystem->WriteDeltaFloat(msg, oldSpeaker->soundParms[3], newSpeaker->soundParms[3]);
	glqMsgSystem->WriteDeltaFloat(msg, oldSpeaker->soundParms[4], newSpeaker->soundParms[4]);
	glqMsgSystem->WriteDeltaFloat(msg, oldSpeaker->soundParms[5], newSpeaker->soundParms[5]);
	glqMsgSystem->WriteDeltaFloat(msg, oldSpeaker->soundParms[6], newSpeaker->soundParms[6]);
	glqMsgSystem->WriteDeltaFloat(msg, oldSpeaker->soundParms[7], newSpeaker->soundParms[7]);
}

/*
 ==================
 SG_WriteDeltaState_Player
 ==================
*/
static void SG_WriteDeltaState_Player (msg_t *msg, entityState_t *oldState, entityState_t *newState){

	player_t	*oldPlayer = (player_t *)oldState->buffer;
	player_t	*newPlayer = (player_t *)newState->buffer;

	glqMsgSystem->WriteDeltaLong(msg, oldPlayer->bounds, newPlayer->bounds);

	glqMsgSystem->WriteDeltaBits(msg, oldPlayer->model, newPlayer->model, ES_MODEL_BITS);

	glqMsgSystem->WriteDeltaBits(msg, oldPlayer->sound, newPlayer->sound, ES_SOUND_SHADER_BITS);
	glqMsgSystem->WriteDeltaLong(msg, oldPlayer->soundTime, newPlayer->soundTime);
	glqMsgSystem->WriteDeltaFloat(msg, oldPlayer->soundParms[0], newPlayer->soundParms[0]);
	glqMsgSystem->WriteDeltaFloat(msg, oldPlayer->soundParms[1], newPlayer->soundParms[1]);
	glqMsgSystem->WriteDeltaFloat(msg, oldPlayer->soundParms[2], newPlayer->soundParms[2]);
	glqMsgSystem->WriteDeltaFloat(msg, oldPlayer->soundParms[3], newPlayer->soundParms[3]);
	glqMsgSystem->WriteDeltaFloat(msg, oldPlayer->soundParms[4], newPlayer->soundParms[4]);
	glqMsgSystem->WriteDeltaFloat(msg, oldPlayer->soundParms[5], newPlayer->soundParms[5]);
	glqMsgSystem->WriteDeltaFloat(msg, oldPlayer->soundParms[6], newPlayer->soundParms[6]);
	glqMsgSystem->WriteDeltaFloat(msg, oldPlayer->soundParms[7], newPlayer->soundParms[7]);
}

/*
 ==================

 ==================
*/
static void SG_WriteDeltaState_Item (msg_t *msg, entityState_t *oldState, entityState_t *newState){

	item_t	*oldItem = (item_t *)oldState->buffer;
	item_t	*newItem = (item_t *)newState->buffer;

	glqMsgSystem->WriteDeltaLong(msg, oldItem->bounds, newItem->bounds);

	glqMsgSystem->WriteDeltaBits(msg, oldItem->model, newItem->model, ES_MODEL_BITS);

	glqMsgSystem->WriteDeltaBits(msg, oldItem->sound, newItem->sound, ES_SOUND_SHADER_BITS);
	glqMsgSystem->WriteDeltaLong(msg, oldItem->soundTime, newItem->soundTime);
	glqMsgSystem->WriteDeltaFloat(msg, oldItem->soundParms[0], newItem->soundParms[0]);
	glqMsgSystem->WriteDeltaFloat(msg, oldItem->soundParms[1], newItem->soundParms[1]);
	glqMsgSystem->WriteDeltaFloat(msg, oldItem->soundParms[2], newItem->soundParms[2]);
	glqMsgSystem->WriteDeltaFloat(msg, oldItem->soundParms[3], newItem->soundParms[3]);
	glqMsgSystem->WriteDeltaFloat(msg, oldItem->soundParms[4], newItem->soundParms[4]);
	glqMsgSystem->WriteDeltaFloat(msg, oldItem->soundParms[5], newItem->soundParms[5]);
	glqMsgSystem->WriteDeltaFloat(msg, oldItem->soundParms[6], newItem->soundParms[6]);
	glqMsgSystem->WriteDeltaFloat(msg, oldItem->soundParms[7], newItem->soundParms[7]);
}

/*
 ==================

 ==================
*/
static void SG_WriteDeltaState_Monster (msg_t *msg, entityState_t *oldState, entityState_t *newState){

	monster_t	*oldMonster = (monster_t *)oldState->buffer;
	monster_t	*newMonster = (monster_t *)newState->buffer;

	glqMsgSystem->WriteDeltaLong(msg, oldMonster->bounds, newMonster->bounds);

	glqMsgSystem->WriteDeltaBits(msg, oldMonster->model, newMonster->model, ES_MODEL_BITS);

	glqMsgSystem->WriteDeltaBits(msg, oldMonster->sound, newMonster->sound, ES_SOUND_SHADER_BITS);
	glqMsgSystem->WriteDeltaLong(msg, oldMonster->soundTime, newMonster->soundTime);
	glqMsgSystem->WriteDeltaFloat(msg, oldMonster->soundParms[0], newMonster->soundParms[0]);
	glqMsgSystem->WriteDeltaFloat(msg, oldMonster->soundParms[1], newMonster->soundParms[1]);
	glqMsgSystem->WriteDeltaFloat(msg, oldMonster->soundParms[2], newMonster->soundParms[2]);
	glqMsgSystem->WriteDeltaFloat(msg, oldMonster->soundParms[3], newMonster->soundParms[3]);
	glqMsgSystem->WriteDeltaFloat(msg, oldMonster->soundParms[4], newMonster->soundParms[4]);
	glqMsgSystem->WriteDeltaFloat(msg, oldMonster->soundParms[5], newMonster->soundParms[5]);
	glqMsgSystem->WriteDeltaFloat(msg, oldMonster->soundParms[6], newMonster->soundParms[6]);
	glqMsgSystem->WriteDeltaFloat(msg, oldMonster->soundParms[7], newMonster->soundParms[7]);
}

/*
 ==================

 ==================
*/
static void SG_WriteDeltaState_Projectile (msg_t *msg, entityState_t *oldState, entityState_t *newState){

	projectile_t	*oldProjectile = (projectile_t *)oldState->buffer;
	projectile_t	*newProjectile = (projectile_t *)newState->buffer;
}

/*
 ==================

 ==================
*/
static void SG_WriteDeltaState (msg_t *msg, entityState_t *oldState, entityState_t *newState){

	int		i;

	// Write the position and orientation
	glqMsgSystem->WriteDeltaByte(msg, oldState->origin.type, newState->origin.type);
	glqMsgSystem->WriteDeltaLong(msg, oldState->origin.time, newState->origin.time);
	glqMsgSystem->WriteDeltaLong(msg, oldState->origin.duration, newState->origin.duration);
	glqMsgSystem->WriteDeltaVec3(msg, oldState->origin.base, newState->origin.base);
	glqMsgSystem->WriteDeltaVec3(msg, oldState->origin.delta, newState->origin.delta);

	glqMsgSystem->WriteDeltaByte(msg, oldState->angles.type, newState->angles.type);
	glqMsgSystem->WriteDeltaLong(msg, oldState->angles.time, newState->angles.time);
	glqMsgSystem->WriteDeltaLong(msg, oldState->angles.duration, newState->angles.duration);
	glqMsgSystem->WriteDeltaAngles(msg, oldState->angles.base, newState->angles.base);
	glqMsgSystem->WriteDeltaAngles(msg, oldState->angles.delta, newState->angles.delta);

	// Write the binding information
	glqMsgSystem->WriteDeltaBits(msg, oldState->bindMaster, newState->bindMaster, ES_BIND_MASTER_BITS);
	glqMsgSystem->WriteBool(msg, newState->bindOrientated);

	// Write the event queue
	glqMsgSystem->WriteDeltaLong(msg, oldState->eventSequence, newState->eventSequence);

	for (i = 0; i < MAX_EVENTS; i++){
		glqMsgSystem->WriteDeltaByte(msg, oldState->eventTypes[i], newState->eventTypes[i]);
		glqMsgSystem->WriteDeltaWord(msg, oldState->eventParms[i], newState->eventParms[i]);
	}

	// Write the state buffer
	switch (newState->type){
	case ET_GENERAL:
		SG_WriteDeltaState_General(msg, oldState, newState);
		break;
	case ET_MOVER:
		SG_WriteDeltaState_Mover(msg, oldState, newState);
		break;
	case ET_BEAM:
		SG_WriteDeltaState_Beam(msg, oldState, newState);
		break;
	case ET_LIGHT:
		SG_WriteDeltaState_Light(msg, oldState, newState);
		break;
	case ET_SPEAKER:
		SG_WriteDeltaState_Speaker(msg, oldState, newState);
		break;
	case ET_PLAYER:
		SG_WriteDeltaState_Player(msg, oldState, newState);
		break;
	case ET_ITEM:
		SG_WriteDeltaState_Item(msg, oldState, newState);
		break;
	case ET_MONSTER:
		SG_WriteDeltaState_Monster(msg, oldState, newState);
		break;
	case ET_PROJECTILE:
		SG_WriteDeltaState_Projectile(msg, oldState, newState);
		break;
	default:
		G_Error("SG_WriteDeltaState: bad entity type (%i)", newState->type);
	}
}


/*
 ==============================================================================

 PLAYER AND ENTITY STATES

 ==============================================================================
*/


/*
 ==================

 ==================
*/
void SG_WriteDeltaPlayerState (msg_t *msg, playerState_t *oldState, playerState_t *newState){

	playerState_t	nullState;
	int				i;

	if (!oldState){
		oldState = &nullState;
		glqMemory->Fill(oldState, 0, sizeof(playerState_t));
	}

	glqMsgSystem->WriteDeltaBits(msg, oldState->pmType, newState->pmType, PS_MOVE_TYPE_BITS);
	glqMsgSystem->WriteDeltaBits(msg, oldState->pmFlags, newState->pmFlags, PS_MOVE_FLAGS_BITS);
	glqMsgSystem->WriteDeltaBits(msg, oldState->pmTime, newState->pmTime, PS_MOVE_TIME_BITS);
	glqMsgSystem->WriteDeltaBits(msg, oldState->clientNum, newState->clientNum, 6);
	glqMsgSystem->WriteDeltaBits(msg, oldState->flags, newState->flags, ES_FLAGS_BITS);
	glqMsgSystem->WriteDeltaVec3(msg, oldState->origin, newState->origin);
	glqMsgSystem->WriteDeltaVec3(msg, oldState->velocity, newState->velocity);
	glqMsgSystem->WriteDeltaLong(msg, oldState->bounds, newState->bounds);
	glqMsgSystem->WriteDeltaFloat(msg, oldState->viewHeight, newState->viewHeight);
	glqMsgSystem->WriteDeltaLong(msg, oldState->weaponType, newState->weaponType);
	glqMsgSystem->WriteDeltaLong(msg, oldState->weaponFrame, newState->weaponFrame);
	glqMsgSystem->WriteDeltaAngles16(msg, oldState->viewAngles, newState->viewAngles);
	glqMsgSystem->WriteDeltaAngles16(msg, oldState->deltaAngles, newState->deltaAngles);
	glqMsgSystem->WriteDeltaLong(msg, oldState->userCmdTime, newState->userCmdTime);
	glqMsgSystem->WriteDeltaBits(msg, oldState->groundEntityNum, newState->groundEntityNum, ES_NUMBER_BITS);

	glqMsgSystem->WriteDeltaLong(msg, oldState->eventSequence, newState->eventSequence);

	for (i = 0; i < MAX_EVENTS; i++){
		glqMsgSystem->WriteDeltaByte(msg, oldState->eventTypes[i], newState->eventTypes[i]);
		glqMsgSystem->WriteDeltaWord(msg, oldState->eventParms[i], newState->eventParms[i]);
	}
}

/*
 ==================
 SG_WriteDeltaEntityState
 ==================
*/
void SG_WriteDeltaEntityState (msg_t *msg, entityState_t *oldState, entityState_t *newState, bool force){

	// A NULL newState is a delta remove
	if (newState == NULL){
		glqMsgSystem->WriteBits(msg, oldState->number, ES_NUMBER_BITS);
		glqMsgSystem->WriteBits(msg, 1, 1);
		return;
	}

	// Check for changes
	if (glqMemory->Compare(oldState, newState, sizeof(entityState_t))){
		// Nothing changed
		if (!force)
			return;		// Nothing at all

		glqMsgSystem->WriteBits(msg, newState->number, ES_NUMBER_BITS);
		glqMsgSystem->WriteBits(msg, 0, 1);		// Not removed
		glqMsgSystem->WriteBits(msg, 0, 1);		// No delta

		return;
	}

	glqMsgSystem->WriteBits(msg, newState->number, ES_NUMBER_BITS);
	glqMsgSystem->WriteBits(msg, 0, 1);		// Not removed
	glqMsgSystem->WriteBits(msg, 1, 1);		// We have a delta

	glqMsgSystem->WriteBits(msg, newState->type, ES_TYPE_BITS);
	glqMsgSystem->WriteBits(msg, newState->flags, ES_FLAGS_BITS);

	glqMsgSystem->WriteBits(msg, newState->weaponType, ES_FLAGS_BITS + 5);		// FIXME!!!

	glqMsgSystem->WriteBits(msg, newState->frame, ES_FLAGS_BITS + 10);		// FIXME!!!

	// Delta compress the entity state
	SG_WriteDeltaState(msg, oldState, newState);
}